package com.ruoyi.security.service.craw.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.ruoyi.security.constant.ApiConstant;
import com.ruoyi.security.response.SecurityDayMinuteTradeResponse;
import com.ruoyi.security.service.craw.ICrawSecurityDayMinuteTradeService;
import com.ruoyi.security.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author yeoman
 * @date 2025/4/30 18:19
 * @description 抓取证券日分钟交易服务
 */
@Slf4j
@Service
@SuppressWarnings("all")
public class CrawSecurityDayMinuteTradeServiceImpl implements ICrawSecurityDayMinuteTradeService {

    @Autowired
    private ApplicationContext applicationContext;
    
    /**
     * 使用 ThreadLocal 为每个线程创建独立的 ChromeDriver 实例
     * 解决多线程并发访问时的线程安全问题
     */
    private static final ThreadLocal<ChromeDriver> chromeDriverThreadLocal = new ThreadLocal<>();
    
    /**
     * 获取当前线程的 ChromeDriver 实例
     */
    private ChromeDriver getChromeDriver() {
        ChromeDriver driver = chromeDriverThreadLocal.get();
        if (driver == null) {
            // 从 Spring 容器获取新的 ChromeDriver 实例（PROTOTYPE 作用域）
            driver = applicationContext.getBean(ChromeDriver.class);
            chromeDriverThreadLocal.set(driver);
            log.debug("为线程 {} 创建新的 ChromeDriver 实例", Thread.currentThread().getName());
        }
        return driver;
    }
    
    /**
     * 清理当前线程的 ChromeDriver 实例
     */
    private void removeChromeDriver() {
        ChromeDriver driver = chromeDriverThreadLocal.get();
        if (driver != null) {
            try {
                driver.quit();
            } catch (Exception e) {
                log.error("关闭 ChromeDriver 失败", e);
            }
            chromeDriverThreadLocal.remove();
        }
    }
    
    /**
     * 应用关闭时清理所有线程的 ChromeDriver
     */
    @PreDestroy
    public void destroy() {
        // 注意：ThreadLocal 中的实例需要在各自线程中清理
        // 这里只能清理当前线程的实例
        removeChromeDriver();
    }

    public List<SecurityDayMinuteTradeResponse> doCraw(String symbol, String date) {
        ChromeDriver chromeDriver = getChromeDriver();
        List<SecurityDayMinuteTradeResponse> minuteTradeList = null;
        try {
            chromeDriver.get(String.format(ApiConstant.SECURITY_MINUTE_TRADE_URL, symbol, date));
            WebElement element = chromeDriver.findElement(By.xpath("//*[@id=\"chart\"]/div"));
            element.click();
            element.sendKeys(Keys.ARROW_RIGHT);
            minuteTradeList = getMinuteTradeList(element, date, chromeDriver);
        } catch (Exception e) {
            log.error("获取股票【{}】【{}】分钟数据异常", symbol, date, e);
            return null;
        }
        return minuteTradeList;
    }

    private List<SecurityDayMinuteTradeResponse> getMinuteTradeList(WebElement element, String date, ChromeDriver chromeDriver){
        Set<String> tradeTimeSet = Sets.newHashSet();
        List<SecurityDayMinuteTradeResponse> minuteTradeList = Lists.newArrayList();
        while (tradeTimeSet.size() < ApiConstant.SECURITY_MINUTE_TRADE_COUNT){
            SecurityDayMinuteTradeResponse tradeResponse = minuteTrade(chromeDriver);
            // 交易时间 2024/11/25/一11:30
            String time = tradeResponse.getTime();
            String resDate = time.substring(0, 4) + "-" + time.substring(5, 7) + "-" + time.substring(8, 10);
            if (!date.equals(resDate)) {
                return null;
            }
            if (!tradeTimeSet.contains(tradeResponse.getTime())) {
                minuteTradeList.add(tradeResponse);
                tradeTimeSet.add(tradeResponse.getTime());
            }
            element.sendKeys(Keys.ARROW_RIGHT);
        }
        return minuteTradeList.stream().sorted(Comparator.comparing(item -> item.getTime().substring(12, 17))).collect(Collectors.toList());
    }

    private SecurityDayMinuteTradeResponse minuteTrade(ChromeDriver chromeDriver){
        SecurityDayMinuteTradeResponse response = new SecurityDayMinuteTradeResponse();
        // 交易时间 2024/11/25/一11:30
        String time = chromeDriver.findElement(By.xpath("//*[@id=\"chart\"]/div/div[5]/table/tbody/tr[1]/th")).getText();
        response.setTime(time);
        // 名称
        String name = chromeDriver.findElement(By.xpath("//*[@id=\"chart\"]/div/div[5]/table/thead/tr/th/span")).getText();
        response.setName(name);
        // 价格
        String price = chromeDriver.findElement(By.xpath("//*[@id=\"chart\"]/div/div[5]/table/tbody/tr[2]/td")).getText();
        response.setPrice(price);
        // 均价
        String averagePrice = chromeDriver.findElement(By.xpath("//*[@id=\"chart\"]/div/div[5]/table/tbody/tr[3]/td")).getText();
        response.setAveragePrice(averagePrice);
        // 涨跌
        String riseFall = chromeDriver.findElement(By.xpath("//*[@id=\"chart\"]/div/div[5]/table/tbody/tr[4]/td")).getText();
        response.setRiseFall(riseFall);
        // 成交量
        String volume = chromeDriver.findElement(By.xpath("//*[@id=\"chart\"]/div/div[5]/table/tbody/tr[5]/td")).getText();
        response.setVolume(volume);
        log.info("minuteTrade response: {}", response.getTime() + " " + JSON.toJSON(response));
        return response;
    }

    @Override
    public List<SecurityDayMinuteTradeResponse> crawList(String symbol, List<String> dateList) {
        List<SecurityDayMinuteTradeResponse> list = Lists.newArrayList();
        for (String date : dateList) {
            List<SecurityDayMinuteTradeResponse> minuteTradeList = doCraw(symbol, date);
            if (CollectionUtils.isNotEmpty(minuteTradeList)) {
                list.addAll(minuteTradeList);
            }

        }
        return list;
    }

}
